start_kernel part II
lock_kernel get big kernel lock.
In operating systems, a giant lock, also known as a big-lock or kernel-lock, is a lock that may be used in the kernel to provide concurrency control required by symmetric multiprocessing (SMP) systems.
A giant lock is a solitary global lock that is held whenever a thread enters kernel space and released when the thread returns to user space; a system call is the archetypal example. In this model, threads in user space can run concurrently on any available processors or processor cores, but no more than one thread can run in kernel space; any other threads that try to enter kernel space are forced to wait. In other words, the giant lock eliminates all concurrency in kernel space.
By isolating the kernel from concurrency, many parts of the kernel no longer need to be modified to support SMP. However, as in giant-lock SMP systems only one processor can run the kernel code at a time, performance for applications spending significant amounts of time in the kernel is not much improved. Accordingly, the giant-lock approach is commonly seen as a preliminary means of bringing SMP support to an operating system, yielding benefits only in user space. Most modern operating systems use a fine-grained locking approach.
Linux kernel 2.6.39 removed the final part of the BKL, the whole BKL locking mechanism. BKL is now finally totally gone!
tick_init initialize the tick control, register tick notifier for clock event devices, the tick notifier defined in code kernel/time/tick-common.c line 363.
boot_cpu_init activate the first cpu, all routines involved in boot_cpu_init finally call cpumask_set_cpu to set specified bit of following cpu mask as ture, every bit of a dedicated cpu mask represent a cpu.
cpu_possible_mask
cpu_online_mask
cpu_present_mask
cpu_active_mask
page_address_init intialize page_address_pool, it's freelist of page_address_map, add list of all elements in page_address_maps to freelist, array length of page_address_maps in this kernel image is
(gdb) p sizeof(page_address_maps)/sizeof(page_address_maps[0])
$3 = 512
initialize page address hash table page_address_htable and lock of page_address_pool.
setup_arch the most important routine involved in start_kernel, every architecture has its own implementation for it.
- Initialize common cpu data of all cpus, source information of cpu initialized before start_kernel in arch/x86/kernel/head_32.S.
- We don't config CONFIG_X86_VISWS, the defination of function
visws_early_detectis NULL. - CONFIG_VMI is not configured, the defination of function
vmi_initis same asvisws_early_detect. early_cpu_initintialize cpu information, it firstly get information of kernel support cpu, listed as follow, after that identify the cpu it used in this linux initialization withearly_identify_cpu.
kernel supports cpu listed as follow:
(gdb) p count
$1 = 7
(gdb) p cpu_devs[0]
$2 = (const struct cpu_dev *) 0xc1687140 <intel_cpu_dev>
(gdb) p cpu_devs[0].c_ident
$3 = {0xc15d066a "GenuineIntel", 0x0}
(gdb) p cpu_devs[1]
$4 = (const struct cpu_dev *) 0xc1687280 <amd_cpu_dev>
(gdb) p cpu_devs[1].c_ident
$5 = {0xc15d085e "AuthenticAMD", 0x0}
(gdb) p cpu_devs[2]
$6 = (const struct cpu_dev *) 0xc16873c0 <nsc_cpu_dev>
(gdb) p cpu_devs[2].c_ident
$7 = {0xc15d08a5 "Geode by NSC", 0x0}
(gdb) p cpu_devs[3]
$8 = (const struct cpu_dev *) 0xc1687500 <cyrix_cpu_dev>
(gdb) p cpu_devs[3].c_ident
$9 = {0xc15d087f "CyrixInstead", 0x0}
(gdb) p cpu_devs[4]
$10 = (const struct cpu_dev *) 0xc1687720 <centaur_cpu_dev>
(gdb) p cpu_devs[4].c_ident
$11 = {0xc15d0919 "CentaurHauls", 0x0}
(gdb) p cpu_devs[5]
$12 = (const struct cpu_dev *) 0xc1687860 <transmeta_cpu_dev>
(gdb) p cpu_devs[5].c_ident
$13 = {0xc15d093c "GenuineTMx86", 0xc15d0949 "TransmetaCPU"}
(gdb) p cpu_devs[6]
$14 = (const struct cpu_dev *) 0xc16879a0 <umc_cpu_dev>
(gdb) p cpu_devs[6].c_ident
$15 = {0xc15d0956 "UMC UMC UMC", 0x0}
In early_identify_cpu:
a. Check cpu has cpuid instruction enabled or not with have_cpuid_p which further involves flag_is_changeable_p, the entire proceduce of flag_is_changeable_p please take following debug information as reference:
flag_is_changeable_p (flag=2097152) at arch/x86/kernel/cpu/common.c:186
186 asm volatile ("pushfl \n\t"
(gdb) x/10i $eip
=> 0xc146f11f <have_cpuid_p+3>: pushf
0xc146f120 <have_cpuid_p+4>: pushf
0xc146f121 <have_cpuid_p+5>: pop %eax
0xc146f122 <have_cpuid_p+6>: mov %eax,%edx
0xc146f124 <have_cpuid_p+8>: xor $0x200000,%eax
0xc146f129 <have_cpuid_p+13>: push %eax
0xc146f12a <have_cpuid_p+14>: popf
0xc146f12b <have_cpuid_p+15>: pushf
0xc146f12c <have_cpuid_p+16>: pop %eax
0xc146f12d <have_cpuid_p+17>: popf
(gdb) info registers eflags
eflags 0x46 [ PF ZF ]
(gdb) si
0xc146f120 186 asm volatile ("pushfl \n\t"
(gdb)
0xc146f121 186 asm volatile ("pushfl \n\t"
(gdb)
0xc146f122 186 asm volatile ("pushfl \n\t"
(gdb) info registers eax
eax 0x46 70
(gdb) si
0xc146f124 186 asm volatile ("pushfl \n\t"
(gdb) info registers edx
edx 0x46 70
(gdb) si
0xc146f129 186 asm volatile ("pushfl \n\t"
(gdb) info registers eax
eax 0x200046 2097222
(gdb) si
0xc146f12a 186 asm volatile ("pushfl \n\t"
(gdb)
0xc146f12b 186 asm volatile ("pushfl \n\t"
(gdb) info registers eflags
eflags 0x200046 [ PF ZF ID ]
(gdb) si
0xc146f12c 186 asm volatile ("pushfl \n\t"
(gdb)
0xc146f12d 186 asm volatile ("pushfl \n\t"
(gdb) info registers eax
eax 0x200046 2097222
(gdb) si
200 return ((f1^f2) & flag) != 0;
(gdb) info registers eflags
eflags 0x46 [ PF ZF ]
(gdb) info registers eax edx
eax 0x200046 2097222
edx 0x46 70
(gdb) p f1
$16 = 2097222
(gdb) p /x f1
$17 = 0x200046
(gdb) p /x f2
$18 = 0x46
(gdb) p /x flag
$19 = 0x200000
(gdb) p (f1^f2)
$20 = 2097152
(gdb) p /x (f1^f2)
$21 = 0x200000
(gdb) p /x ((f1^f2) & flag)
$22 = 0x200000
b. Get cpu information with cpu_detect in which it involves cpuid instruction finally with different operation id, get verder id with opcode 0 and get processor info and feature bits.
c. Get cpu vender with get_cpu_vendor, vender information in boot_cpu_data is
(gdb) p c->x86_vendor_id
$24 = "GenuineIntel\000\000\000"
d. Get cpu capability with get_cpu_cap, check the cpuid instruction introduction for the result of opecode 1. Finally initialize the scattered cpu feature with init_scattered_cpuid_features.
e. early_init_intel initialize intel cpu. cpu info of our x86 as follow:
(gdb) p *c
$43 = {x86 = 6 '\006', x86_vendor = 0 '\000', x86_model = 6 '\006',
x86_mask = 3 '\003', wp_works_ok = -1 '\377', hlt_works_ok = 1 '\001',
hard_math = 1 '\001', rfu = 0 '\000', fdiv_bug = -1 '\377',
f00f_bug = 0 '\000', coma_bug = 0 '\000', pad0 = 0 '\000',
x86_virt_bits = 32 ' ', x86_phys_bits = 36 '$', x86_coreid_bits = 0 '\000',
extended_cpuid_level = 2147483652, cpuid_level = 4, x86_capability = {
125938681, 0, 0, 0, 2155872257, 0, 0, 0, 0},
x86_vendor_id = "GenuineIntel\000\000\000",
x86_model_id = '\000' <repeats 63 times>, x86_cache_size = 0,
x86_cache_alignment = 32, x86_power = 0, loops_per_jiffy = 0,
llc_shared_map = {{bits = {0}}}, x86_max_cores = 0, apicid = 0,
initial_apicid = 0, x86_clflush_size = 32, booted_cores = 0,
phys_proc_id = 0, cpu_core_id = 0, cpu_index = 0, x86_hyper_vendor = 0}
f. filter_cpuid_features about other features of current cpu.
The result of boot_cpu_data after executed early_cpu_init:
(gdb) p boot_cpu_data
$46 = {x86 = 6 '\006', x86_vendor = 0 '\000', x86_model = 6 '\006',
x86_mask = 3 '\003', wp_works_ok = -1 '\377', hlt_works_ok = 1 '\001',
hard_math = 1 '\001', rfu = 0 '\000', fdiv_bug = -1 '\377',
f00f_bug = 0 '\000', coma_bug = 0 '\000', pad0 = 0 '\000',
x86_virt_bits = 32 ' ', x86_phys_bits = 36 '$', x86_coreid_bits = 0 '\000',
extended_cpuid_level = 2147483652, cpuid_level = 4, x86_capability = {
125873145, 0, 0, 0, 2155872257, 0, 0, 0, 0},
x86_vendor_id = "GenuineIntel\000\000\000",
x86_model_id = '\000' <repeats 63 times>, x86_cache_size = 0,
x86_cache_alignment = 32, x86_power = 0, loops_per_jiffy = 0,
llc_shared_map = {{bits = {0}}}, x86_max_cores = 0, apicid = 0,
initial_apicid = 0, x86_clflush_size = 32, booted_cores = 0,
phys_proc_id = 0, cpu_core_id = 0, cpu_index = 0, x86_hyper_vendor = 0}